package org.Yiran.timetale_re.weather;

import net.minecraft.core.BlockPos;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.phys.AABB;
import net.minecraft.world.phys.shapes.CollisionContext;
import net.minecraft.world.phys.shapes.VoxelShape;
import net.minecraftforge.event.TickEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.common.Mod;
import org.Yiran.timetale_re.Timetale_re;
import org.Yiran.timetale_re.weather.manager.WindManager;
import org.joml.Vector3f;

import java.util.List;

/**
 * 大风天气效果处理器
 * <p>
 * 负责处理大风天气对游戏世界中实体的影响，包括吹动掉落物和影响投掷物轨迹等效果。
 * 通过WindManager获取风力数据，并根据风力等级应用相应的效果。
 * </p>
 *
 * @author Yiran
 * @version 1.0
 */
@Mod.EventBusSubscriber(modid = Timetale_re.MODID)
public class WindyWeather {

    // ================================
    // 事件处理方法
    // ================================

    /**
     * 世界Tick事件处理方法
     * <p>
     * 在每个世界tick时检查是否有大风天气，如果有则应用大风天气效果
     * </p>
     *
     * @param event 世界tick事件对象
     */
    @SubscribeEvent
    public static void onWorldTick(TickEvent.LevelTickEvent event) {
        // 只在服务器端的tick开始时处理
        if (event.phase == TickEvent.Phase.END || event.level.isClientSide() || !(event.level instanceof ServerLevel serverLevel))
            return;

        StormWeatherData weatherData = StormWeatherData.get(serverLevel);

        // 检查是否有大风天气
        if (weatherData.isWindy() && serverLevel.getGameTime() < weatherData.getStormEndTime()) {
            applyWindyWeatherEffects(serverLevel);
        }
    }

    // ================================
    // 大风天气效果应用方法
    // ================================

    /**
     * 应用大风天气效果（吹动掉落物和影响投掷物轨迹）
     * <p>
     * 遍历所有在线玩家，对其附近范围内的实体应用风力效果，
     * 包括掉落物和投掷物等受风力影响的实体
     * </p>
     *
     * @param level 服务器世界对象
     */
    private static void applyWindyWeatherEffects(ServerLevel level) {
        StormWeatherData weatherData = StormWeatherData.get(level);
        int windLevel = weatherData.getWindLevel();

        // 获取所有在线玩家
        List<ServerPlayer> players = level.players();

        // 对每个玩家处理其附近的实体
        for (ServerPlayer player : players) {
            BlockPos playerPos = player.blockPosition();

            // 定义64格范围的边界框
            AABB searchBox = new AABB(
                playerPos.getX() - 64, playerPos.getY() - 64, playerPos.getZ() - 64,
                playerPos.getX() + 64, playerPos.getY() + 64, playerPos.getZ() + 64
            );

            // 获取范围内的所有掉落物
            List<Entity> nearbyItems = level.getEntitiesOfClass(Entity.class, searchBox);

            // 处理每个掉落物
            for (Entity entity : nearbyItems) {
                BlockPos pos = entity.blockPosition();

                // 检查掉落物是否被方块阻挡
                if (isEntityBlockedByBlocks(level, pos, entity)) {
                    continue; // 如果被阻挡，则不应用风力效果
                }

                // 应用风力到掉落物
                var windVector = WindManager.calculateWindVector(level, pos);

                // 根据风力等级应用风力到掉落物
                double forceMultiplier = windLevel * 0.02; // 等级越高风力越大
                entity.push(
                    windVector.x() * forceMultiplier,
                    windVector.y() * forceMultiplier,
                    windVector.z() * forceMultiplier
                );
            }

            // 获取范围内的所有投掷物
            List<Entity> nearbyProjectiles = level.getEntitiesOfClass(Entity.class, searchBox);

            // 处理每个投掷物
            for (Entity entity : nearbyProjectiles) {
                BlockPos pos = entity.blockPosition();

                // 检查投掷物是否被方块阻挡
                if (isEntityBlockedByBlocks(level, pos, entity)) {
                    continue; // 如果被阻挡，则不应用风力效果
                }

                // 应用风力到投掷物
                var windVector = WindManager.calculateWindVector(level, pos);

                // 投掷物受风力影响更大
                double forceMultiplier = windLevel * 0.03; // 等级越高风力越大
                entity.push(
                    windVector.x() * forceMultiplier,
                    windVector.y() * forceMultiplier,
                    windVector.z() * forceMultiplier
                );
            }
        }
    }

    // ================================
    // 实体阻挡检查方法
    // ================================

    /**
     * 检查实体是否被方块阻挡，无法受到风力影响
     * <p>
     * 通过检查实体前方是否有完整的方块来判断是否被阻挡，
     * 如果被阻挡则不应用风力效果
     * </p>
     *
     * @param level  服务器世界对象
     * @param pos    实体位置
     * @param entity 实体对象
     * @return 如果被阻挡返回true，否则返回false
     */
    private static boolean isEntityBlockedByBlocks(ServerLevel level, BlockPos pos, Entity entity) {
        // 获取风向
        Vector3f windVector = WindManager.calculateWindVector(level, pos);

        // 如果风力为0，则没有阻挡的必要
        if (windVector.length() == 0) {
            return false;
        }

        // 计算检查位置（风向的方向）
        double checkDistance = 2.0; // 检查距离
        BlockPos checkPos = new BlockPos(
                (int) (pos.getX() + (int) Math.signum(windVector.x()) * checkDistance),
                (int) (pos.getY() + (int) Math.signum(windVector.y()) * checkDistance),
                (int) (pos.getZ() + (int) Math.signum(windVector.z()) * checkDistance)
        );

        // 检查该位置是否有完整的方块
        BlockState blockState = level.getBlockState(checkPos);
        if (!blockState.isAir()) {
            VoxelShape collisionShape = blockState.getCollisionShape(level, checkPos, CollisionContext.of(entity));
            // 如果碰撞形状是完整的方块，则阻挡风力
            return !collisionShape.isEmpty() && isFullBlockShape(collisionShape);
        }

        return false;
    }

    /**
     * 检查碰撞形状是否为完整的方块
     * <p>
     * 判断给定的碰撞形状是否填满整个方块空间，用于判断方块是否能完全阻挡风力
     * </p>
     *
     * @param shape 碰撞形状对象
     * @return 如果是完整方块返回true，否则返回false
     */
    private static boolean isFullBlockShape(VoxelShape shape) {
        // 检查形状是否填满整个方块空间 (0,0,0 到 1,1,1)
        return shape.bounds().equals(new AABB(0, 0, 0, 1, 1, 1)) && shape.toAabbs().size() == 1;
    }
}